#!/bin/sh
# Bug reports and comments to Igor.Ermolaev@intel.com
base=$(dirname $(readlink -f ${BASH_SOURCE[0]:-$0}))
anchor_isa=${TRACING_SDK_ANCHOR_ISA:-$TRACING_SDK_ANCHOR_ISET}
anchor_isa=${anchor_isa:-AVX}
tpl_match_funcs=( 's/^mkl_(blas|dft|trans)_avx\d*(_mic)?_/mkl_\1_\0_/' )
platforms=(
   'SSE4.2:-wsm::'
   'AVX:-snb::avx'
   'AVX2,FMA:-hsw::avx2'
   'LRB2:-lrb2::'
   'AVX512,AVX3.2,AVX3:-skx::avx512'
   'AVX3.1:-knl::avx512_mic'
   'AVX512,SMADD,QMADD:-knh-sc::avx512_mic'
   'AVX512:-icl::avx512'
)
sfx=${sfx:-$anchor_isa}
tgt_ovr=-"${sfx##*@}";if [ "$tgt_ovr" == "-$sfx" ];then unset tgt_ovr; else sfx="${sfx%@*}"; fi
unset isa
for platform in "${platforms[@]}";do
   saveIFS="$IFS";IFS=':';desc=( $platform );IFS="$saveIFS"
   isas=( ${desc[0]//,/ } )
   for i in "${isas[@]}";do
      if [[ $sfx == $i* ]];then
         if [ ${#i} -gt ${#isa} ] || ( [ ${#i} -eq ${#isa} ] && [ ${desc[1]} == "$tgt_ovr" ] );then
            isa=$i
            tgt=${desc[1]}
            extra=${desc[2]}
            unset match_funcs
            tpl=0
            for sg in ${desc[3]//,/ };do
               re="${tpl_match_funcs[$tpl]//\\0/$sg}"
               bs1='\\\1'
               match_funcs[${#match_funcs[@]}]=`echo "$re"|sed "s/\([()|+?]\)/$bs1/g"|sed "s/[\\]d/[0-9]/g"`
               (( tpl++ ))
            done
         fi
      fi
   done
done
if [ -z "$isa" ];then isa="$sfx"; fi
if [ -n "$tgt_ovr" ];then tgt="$tgt_ovr"; fi
if [ -z "$tgt" ];then tgt="-$sfx"; fi
unset tpl_match_funcs platforms tgt_ovr platform saveIFS desc isas i tpl sg re bs1
if [ $(basename $(readlink -f ${BASH_SOURCE[0]:-$0})) != $(basename $0) ];then return; fi
# ----------------------------------------------------------------------------------------------
nsts=( ${TRACING_SDK_FIND_LOOP_NSTS:-0 1 2} )
if [ ${TRACING_SDK_FIND_LOOP_IDX:-enable} == enable  ];then idxs=( '' ' -v 0' ' -i 0' ); fi
if [ ${TRACING_SDK_FIND_LOOP_IDX:-enable} == only    ];then idxs=( ' -i 0' );            fi
if [ ${TRACING_SDK_FIND_LOOP_IDX:-enable} == +index  ];then idxs=( '' ' -i 0' );         fi
if [ ${TRACING_SDK_FIND_LOOP_IDX:-enable} == vector  ];then idxs=( ' -v 0' );            fi
if [ ${TRACING_SDK_FIND_LOOP_IDX:-enable} == +vector ];then idxs=( '' ' -v 0' );         fi
if [ ${TRACING_SDK_FIND_LOOP_IDX:-enable} == disable ];then idxs=( '' );                 fi

if [ ${TRACING_SDK_FIND_LOOP_PRFC:-enable} == enable  ];then prfs=( ' -p' '' ); fi
if [ ${TRACING_SDK_FIND_LOOP_PRFC:-enable} == only    ];then prfs=( ' -p' );    fi
if [ ${TRACING_SDK_FIND_LOOP_PRFC:-enable} == disable ];then prfs=( '' );       fi

loop_idx=1
loop_cur=1
if [ $# -eq 1 ];then
   if [[ "$1" =~ ^[0-9]+$ ]];then
      loop_idx=$1
      shift
   elif [[ ! "$1" =~ :[0-9]+$ ]];then
      focus=$1
      shift
   fi
elif [ $# -eq 2 ] && [[ ! "$1" =~ :[0-9]+$ ]] && [[ "$2" =~ ^[0-9]+$ ]];then
   focus=$1
   loop_idx=$2
   shift 2
fi
save_IFS=$IFS;IFS='
'
if [ $# -eq 0 ];then
   blocks_list=( `$base/find_hotspots -l` )
else
   blocks_list=( `echo "$@"|sed 's/ -- /\n/g'|$base/find_hotspots` )
fi

echo
disasm=`mktemp`
for l in ${blocks_list[@]};do
   save_IFS2=$IFS;IFS=' '
      blocks=( $l )
      weight=${blocks[0]}
      module=${blocks[1]}
      fname=${blocks[2]}
      blocks=("${blocks[@]:3:((${#blocks[@]}-3))}")
   IFS=$save_IFS2
   if [ -n "$focus" ] && [ "$fname" != $focus ];then continue; fi
   module=`$base/addrs2 base "$module"`

   echo Processing hot loop${fname:+ of $fname} from $(basename ${module%%:[0-9]*}) with weight: $weight
   $base/get_disasm ${module%%:[0-9]*} $fname "${blocks[@]}" >$disasm

   unset no_src
   lines=( `cat $disasm|$base/find_lines "${blocks[@]}"` )
   if [ ${#lines[@]} -eq 0 ];then
      if [ "$TRACING_SDK_FIND_LOOP_IDX" == only ] || [ "$TRACING_SDK_FIND_LOOP_IDX" == vector ];then
         no_src="${IFS}-a"
         lines=( `cat $disasm|$base/find_lines$no_src "${blocks[@]}"` )
      fi
      if [ ${#lines[@]} -eq 0 ];then
         echo
         echo '   'The module was skipped due to missed debug info for hot loop.
         echo '   'If estimation of module is important please use exact call graph analysis.
         echo
         continue
      else
         echo
         echo '   'Warning: no source line info found. Propogation might be wrong.
         echo '            'Manual check is required.
         echo
      fi
   fi
   loop=loop.$isa.s
   for lidx in "${idxs[@]}";do
      for nesting in ${nsts[@]};do
         for perfect in "${prfs[@]}";do
            for line in ${lines[@]};do
               pidx=none
               for (( cidx=0; ; cidx++ ));do
                  lidx=`echo $lidx|sed 's/\(\s\+-[vi]\s\+\)[0-9]\+/\1'$cidx'/'`
                  if [ "$lidx" == "$pidx" ];then break; else pidx="$lidx"; fi
                  save_IFS2=$IFS;IFS=' ';cat $disasm|$base/get_loop $line 5 5 -l -s -f$perfect -n $nesting$lidx >$loop;IFS=$save_IFS2
                  var=$(grep -c ';===' $loop)
                  if [ $var -eq 0 ];then break; fi
                  if [ `cat $loop|$base/find_lines$no_src "${blocks[@]}"|wc -l` -eq 0 ];then continue; fi
                  if [ $var -ne 1 ];then
                     scopes=( `grep -P ';---\s+.+' $loop|sed 's/;---\s\+\(\S\+\).*/\1/'|sort -u` )
                     if [ ${#scopes[@]} -le 1 ];then break; fi
                     save_IFS2=$IFS;IFS=' '
                        for scope in ${scopes[@]};do
                           cat $disasm|$base/get_loop $line 5 5 -l -s -f$perfect -n $nesting$lidx -r $scope >$loop
                           var=$(grep -c ';===' $loop)
                           if [ $var -eq 1 ];then
                              if [ `cat $loop|$base/find_lines$no_src "${blocks[@]}"|wc -l` -ne 0 ];then
                                 break
                              else
                                 var=0
                              fi
                           fi
                        done
                     IFS=$save_IFS2
                  fi
                  if [ $var -ne 1 ];then break; fi
                  if [ $loop_cur -ne $loop_idx ];then
                     echo;echo Remark: Hot loop was skipped due to exact loop\'s index specified \($loop_idx\);echo
                     ((loop_cur++))
                     continue 6
                  fi
                  ranges=( `$base/get_ranges "${blocks[@]}" -r <$loop` )
                  for r in ${ranges[@]};do
                     save_IFS2=$IFS;IFS=' ';range=( $r );IFS=$save_IFS2
                     if [[ "$lidx" == " -v "* ]];then
                        echo
                        echo '   'Warning: the loop has been selected as one with SIMD instructions inside
                        echo '            'from list of loops for given source line. Manual examination of
                        echo '            'results is highly recommended.
                        echo
                     fi
                     if [[ "$lidx" == " -i "* ]];then
                        echo
                        echo '   'Warning: the loop has been selected as one with specific position from
                        echo '            list of loops for given  source line.  Manual examination  of'
                        echo '            'results is highly recommended.
                        echo
                     fi
                     echo
                     echo 'First, try do whole loop tracing using the following command.'
                     echo
                     echo '   'sde ${tgt:+$tgt }-pinlit -trace-mt -start-address ${range[1]} -stop-address ${range[3]} -length 100000000 ${extra:+$extra }-moi $(basename $module) -- '$@'
                     echo
                     echo 'If number of instructions in trace is  less than 30M, choose'
                     echo 'appropriate  skip  count  for  -stop-address  to get  30-70M'
                     echo 'instructions in trace.'
                     echo
                     echo 'If command above terminates due to 100M instructions limit reached,'
                     echo 'then try the following command choosing appropriate skip count for'
                     echo '-stop-address to have 30-70M instructions in trace.'
                     echo
                     echo '   'sde ${tgt:+$tgt }-pinlit -trace-mt -start-address ${range[1]} -stop-address ${range[2]}:1 -length 100000000 ${extra:+$extra }-moi $(basename $module) -- '$@'
                     echo
                     if [ "${range[0]}" != "-1" ];then
                     echo "If you'd like to skip several loops instances use the"
                     echo 'following command:'
                     echo
                     echo '   'sde ${tgt:+$tgt }-pinlit -trace-mt -start-address ${range[0]}:1 -stop-address ${range[3]} -length 100000000 ${extra:+$extra }-moi $(basename $module) -- '$@'
                     echo
                     echo 'If command above terminates due to 100M instructions limit reached,'
                     echo 'then try the following command choosing appropriate skip count for'
                     echo '-stop-address to have 30-70M instructions in trace.'
                     echo
                     echo '   'sde ${tgt:+$tgt }-pinlit -trace-mt -start-address ${range[0]}:1 -stop-address ${range[2]}:1 -length 100000000 ${extra:+$extra }-moi $(basename $module) -- '$@'
                     echo
                     fi
                     break 7
                  done
               done
            done
         done
      done
   done
   echo
   echo '   'The module was skipped because of inability to find source line which
   echo '   'corresponds to only this hot loop \(mostly because of inlining\).
   echo '   'If estimation of module is important please use exact call graph analysis.
   echo
done
IFS=$save_IFS

rm $disasm
if [ -z "$range" ];then
   echo >&2
   echo Can\'t find appropriate source line for any hot loop from profile. >&2
   echo Exact call graph needs to be built for function boundaries extraction. >&2
   echo >&2
fi
